// LPMolecViewer.h : Declaration of the CLPMolecViewer

#ifndef __LPMOLECVIEWER_H_
#define __LPMOLECVIEWER_H_

#include "resource.h"       // main symbols
#include <atlctl.h>
#include "LPAtom.h"
#include "LPMolecule.h"
#include "LPConnector.h"

#include <gl/gl.h>
#include <gl/glu.h>

#include "stdio.h"
#include "math.h"
#include "LPMolecViewerControlCP.h"


#define SPHERE_RESOLUTION_LIMIT 50
#define CONNECTOR_RESOLUTION_LIMIT 50

/////////////////////////////////////////////////////////////////////////////
// CLPMolecViewer
class ATL_NO_VTABLE CLPMolecViewer : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public IDispatchImpl<ILPMolecViewer, &IID_ILPMolecViewer, &LIBID_LPMOLECVIEWERCONTROLLib>,
	public CComControl<CLPMolecViewer>,
	public IPersistStreamInitImpl<CLPMolecViewer>,
	public IOleControlImpl<CLPMolecViewer>,
	public IOleObjectImpl<CLPMolecViewer>,
	public IOleInPlaceActiveObjectImpl<CLPMolecViewer>,
	public IViewObjectExImpl<CLPMolecViewer>,
	public IOleInPlaceObjectWindowlessImpl<CLPMolecViewer>,
	public IPersistStorageImpl<CLPMolecViewer>,
	public ISpecifyPropertyPagesImpl<CLPMolecViewer>,
	public IQuickActivateImpl<CLPMolecViewer>,
	public IDataObjectImpl<CLPMolecViewer>,
	public IProvideClassInfo2Impl<&CLSID_LPMolecViewer, &DIID__ILPMolecViewerEvents, &LIBID_LPMOLECVIEWERCONTROLLib>,
	public IPropertyNotifySinkCP<CLPMolecViewer>,
	public CComCoClass<CLPMolecViewer, &CLSID_LPMolecViewer>,
	public IObjectSafetyImpl<CLPMolecViewer,INTERFACESAFE_FOR_UNTRUSTED_CALLER
         |INTERFACESAFE_FOR_UNTRUSTED_DATA>
{
public:
	CLPMolecViewer();

DECLARE_REGISTRY_RESOURCEID(IDR_LPMOLECVIEWER)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CLPMolecViewer)
	COM_INTERFACE_ENTRY(ILPMolecViewer)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IViewObjectEx)
	COM_INTERFACE_ENTRY(IViewObject2)
	COM_INTERFACE_ENTRY(IViewObject)
	COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY(IOleInPlaceObject)
	COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
	COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
	COM_INTERFACE_ENTRY(IOleControl)
	COM_INTERFACE_ENTRY(IOleObject)
	COM_INTERFACE_ENTRY(IPersistStreamInit)
	COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
	COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
	COM_INTERFACE_ENTRY(IQuickActivate)
	COM_INTERFACE_ENTRY(IPersistStorage)
	COM_INTERFACE_ENTRY(IDataObject)
	COM_INTERFACE_ENTRY(IProvideClassInfo)
	COM_INTERFACE_ENTRY(IProvideClassInfo2)
	COM_INTERFACE_ENTRY(IObjectSafety) //IObjectSafety
END_COM_MAP()

BEGIN_PROP_MAP(CLPMolecViewer)
	PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
	PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
	// Example entries
	// PROP_ENTRY("Property Description", dispid, clsid)
	// PROP_PAGE(CLSID_StockColorPage)
END_PROP_MAP()


BEGIN_MSG_MAP(CLPMolecViewer)
	CHAIN_MSG_MAP(CComControl<CLPMolecViewer>)
	DEFAULT_REFLECTION_HANDLER()
	MESSAGE_HANDLER(WM_CREATE, OnCreate)
	MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
	MESSAGE_HANDLER(WM_SIZE, OnSize)
	MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
	MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUP)
	MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
	MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
	MESSAGE_HANDLER(WM_TIMER, OnTimer)
	MESSAGE_HANDLER(WM_PAINT, OnPaint)
	MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
END_MSG_MAP()
// Handler prototypes:
//  LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
//  LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
//  LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);



// IViewObjectEx
	DECLARE_VIEW_STATUS(VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE)

// ILPMolecViewer
public:
	STDMETHOD(TestShowMessageBox)();
	STDMETHOD(SaveMolToPNG)(BSTR filename);
	STDMETHOD(GetBitmap)(HBITMAP *bmp);
	STDMETHOD(get_PerspectiveAngle)(/*[out, retval]*/ double *pVal);
	STDMETHOD(put_PerspectiveAngle)(/*[in]*/ double newVal);
	double m_perspective;

	void computeGARotorFromAngles();
	void rotateGAZaxis(double AngleRad);
	void rotateGAXaxis(double AngleDeg);
	void rotateGAYaxis(double AngleRad);
	//Rotation of the model is described by geometric algebra rotations
	double m_GARotR0;
	double m_GARotR12;
	double m_GARotR13;
	double m_GARotR23;

	bool m_isModelDirty;
	bool m_firstdraw;
	STDMETHOD(get_AnglePhi)(/*[out, retval]*/ double *pVal);
	STDMETHOD(put_AnglePhi)(/*[in]*/ double newVal);
	STDMETHOD(get_AnglePsi)(/*[out, retval]*/ double *pVal);
	STDMETHOD(put_AnglePsi)(/*[in]*/ double newVal);
	STDMETHOD(get_AngleTheta)(/*[out, retval]*/ double *pVal);
	STDMETHOD(put_AngleTheta)(/*[in]*/ double newVal);
	double m_zoomfactor;
	STDMETHOD(get_ZoomFactor)(/*[out, retval]*/ double *pVal);
	STDMETHOD(put_ZoomFactor)(/*[in]*/ double newVal);
	STDMETHOD(get_AutoConnect)(/*[out, retval]*/ BOOL *pVal);
	STDMETHOD(put_AutoConnect)(/*[in]*/ BOOL newVal);
	BOOL m_autoconnect;
	void DisableOpenGL();
	void EnableOpenGL();
	int m_nTimer;
	bool m_isglinit;
	STDMETHOD(get_TimerInterval)(/*[out, retval]*/ short *pVal);
	STDMETHOD(put_TimerInterval)(/*[in]*/ short newVal);
	int m_TimerInterval;
	STDMETHOD(get_ConnectorResolution)(/*[out, retval]*/ short *pVal);
	STDMETHOD(put_ConnectorResolution)(/*[in]*/ short newVal);
	STDMETHOD(get_SphereResolution)(/*[out, retval]*/ short *pVal);
	STDMETHOD(put_SphereResolution)(/*[in]*/ short newVal);
	//bool m_bDrawRough;
	STDMETHOD(get_Animate)(/*[out, retval]*/ BOOL *pVal);
	STDMETHOD(put_Animate)(/*[in]*/ BOOL newVal);
	BOOL m_bAnimate;
	void animationStep();
	HDC m_hdc;
	STDMETHOD(get_FileLocation)(/*[out, retval]*/ BSTR *pVal);
	STDMETHOD(put_FileLocation)(/*[in]*/ BSTR newVal);

	// IPersistStreamImpl
	STDMETHOD(Load)(IStream *pStream);
	STDMETHOD(Save)(IStream *pStream, BOOL fClearDirty);
	STDMETHOD(GetSizeMax)(ULARGE_INTEGER* pcbSize);

	int m_wMousePos;
	void InitDisplayLists();
	short m_yMousePos;
	short m_xMousePos;
	bool m_bMouseCaptured;
	int m_height;
	int m_width;
	bool m_bFileOpen;
	void drawGLConnector(LPConnector *Connector, int resol);
	void drawGLAtom(LPAtom *Atom, int resol);
	void lightGLScene();
	void cameraGLScene();
	void rotateGLScene();
	void rotateGLSceneGA();

	void drawGLMolecule();
	void drawGLScene();
	LPMolecule *m_Molecule;
	void ReadMolFile(TCHAR *my_file);
	void InitGLScene();
	void projectionGLScene(double angle, double nearplane, double farplane);

	double M_2PI;
	double m_CameraDist;
	GLuint m_molDispList;
	GLuint m_molDispListRough;
	GLUquadricObj *m_qobj;
	double m_theta;
	double m_psi;
	double m_phi;
	int m_connectorResolution;
	int m_sphereResolution;
	TCHAR *m_FileLocation;
	void CreateContext(HDC hdc);

	BOOL SetupPixelFormat(HDC hdc);
	HGLRC m_hRC;	// OpenGL rendering context

	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	HRESULT OnDraw(ATL_DRAWINFO& di);
	
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		SetFocus();
		SetCapture();
		m_bMouseCaptured = TRUE;
		m_xMousePos = (short)LOWORD(lParam);  // horizontal position of cursor
		m_yMousePos = (short)HIWORD(lParam);  // vertical position of cursor
		//Reduces resolution of molecule
		//m_bDrawRough=true;
		return 0;

	}
	LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		m_bMouseCaptured = FALSE;
		ReleaseCapture();
		//m_bDrawRough=false;
		//OnDraw(NULL);
		drawGLScene();
		//SwapBuffers(m_hdc);
		//FireViewChange();
		return 0;
	}
	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		if (m_bMouseCaptured)
		{
			int xPos = (short)LOWORD(lParam);  // horizontal position of cursor
			int yPos = (short)HIWORD(lParam);  // vertical position of cursor
			
			//adjust angles
			//m_theta += (float)(xPos-m_xMousePos);
			//m_phi+=(float)(m_yMousePos - yPos)/-2;
			//m_psi+=(float)(yPos-m_yMousePos);
			
			//Adjust rotations
			rotateGAYaxis((double)(xPos-m_xMousePos)*M_2PI/360);
			rotateGAXaxis((double)(m_yMousePos-yPos)*M_2PI/360);
			m_xMousePos = xPos;
			m_yMousePos = yPos;
			drawGLScene();
			//FireViewChange();
		}
		return 0;
	}

	LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		SetFocus();
		//If you want to use mouse events need to focus (SetFocus())
		int zDelta = (short)HIWORD(wParam);  // vertical position of cursor
		//int zDelta = (int)wParam;		
		
		//Change to adjust m_zoomfactor
		m_zoomfactor-=((double)zDelta)/1000;
		//m_CameraDist-=zDelta/100;

		//m_phi++;
		drawGLScene();
		//FireViewChange();

		return 0;
	}
	LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		if (m_bAnimate){
			//m_bDrawRough=true;
			animationStep();
			drawGLScene();
			//FireViewChange();
		}
		return 0;
	}
	LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		drawGLScene();
		//FireViewChange();
		return 0;
	}
	LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		// Double click will either stop or start animation
		BOOL b;
		this->get_Animate(&b);
		if (b){
			this->put_Animate(false);
		}else{
			this->put_Animate(true);
		}
		return 0;
	}
};
#endif //__LPMOLECVIEWER_H_
